import csv
from collections import OrderedDict
from ds.event import Event
from bqgraph.base import (
    BaseCell,
    BidCell,
    AskCell,
    create_check_menu_list,
    EVENT_IID_CHANGED,
)
from bqgraph.qt import *
from bqgraph.constant import *
from bqgraph.functions import safe_unicode, ss_future, save_ss_instruments
from ds.event_type import *


class TickTable(QtWidgets.QTableWidget):
    """市场即时行情监控组件"""

    signal = pyqtSignal(type(Event))
    iid_changed_signal = pyqtSignal(type(Event))
    tick_signal = pyqtSignal(type(Event))

    def __init__(
        self,
        data_event,
        parent=None,
        keys=None,
        mainWindow=None,
        name="market_tick_table",
    ):
        """Constructor"""
        super(TickTable, self).__init__(parent=parent)
        self.data_event = data_event
        # 主窗口
        self.mainWindow = mainWindow
        self.current_instrument_id = ""
        self.name = name
        self.instruments = []

        # 保存表头标签用
        self.headerDict = OrderedDict()  # 有序字典，key是英文名，value是对应的配置字典
        self.headerList = []  # 对应self.headerDict.keys()
        self.keys = set()
        if keys:
            self.keys = keys
            self.instruments = list(self.keys)
        # print(self.displaySymbolSet)

        # 保存相关数据用
        self.dataDict = {}  # 字典，key是字段对应的数据，value是保存相关单元格的字典
        self.dataKey = "InstrumentID"  # 字典键对应的数据字段

        # 监控的事件类型
        self.eventType = "Tick."

        # 字体
        self.font = None

        # 保存数据对象到单元格
        self.saveData = True

        # 默认不允许根据表头进行排序，需要的组件可以开启
        self.sorting = False

        # 设置表头有序字典
        d = OrderedDict()
        d["InstrumentID"] = {"chinese": "合约代码", "cellType": BaseCell, "display": 1}
        d["LastPrice"] = {"chinese": "最新价", "cellType": BaseCell, "display": 1}
        # d['PreClosePrice'] = {'chinese': u'昨收盘价', 'cellType': BaseCell, 'display': 1}
        d["Volume"] = {"chinese": "成交量", "cellType": BaseCell, "display": 1}
        d["OpenInterest"] = {"chinese": "持仓量", "cellType": BaseCell, "display": 1}
        d["OpenPrice"] = {"chinese": "开盘价", "cellType": BaseCell, "display": 1}
        d["HighestPrice"] = {"chinese": "最高价", "cellType": BaseCell, "display": 1}
        d["LowestPrice"] = {"chinese": "最低价", "cellType": BaseCell, "display": 1}
        d["BidPrice1"] = {
            "chinese": "买一价",
            "cellType": BidCell,
            "display": 1,
        }  # BidCell
        d["BidVolume1"] = {
            "chinese": "买一量",
            "cellType": BidCell,
            "display": 1,
        }  # BidCell
        d["AskPrice1"] = {
            "chinese": "卖一价",
            "cellType": AskCell,
            "display": 1,
        }  # AskCell
        d["AskVolume1"] = {
            "chinese": "卖一量",
            "cellType": AskCell,
            "display": 1,
        }  # AskCell
        # d['PreClosePrice'] = {'chinese': '昨收', 'cellType': BaseCell, 'display': 1}
        d["PreSettlementPrice"] = {
            "chinese": "昨结",
            "cellType": BaseCell,
            "display": 1,
        }
        d["PreOpenInterest"] = {"chinese": "昨持", "cellType": BaseCell, "display": 1}
        d["UpdateTime"] = {"chinese": "时间", "cellType": BaseCell, "display": 1}
        # d['gatewayName'] = {'chinese': u'接口', 'cellType': BaseCell, 'display': 1}

        self.setHeaderDict(d)

        # 设置数据键
        self.setDataKey("InstrumentID")

        # 设置监控事件类型
        # self.setEventType(EVENT_RtnDepthMarketData)

        # 设置字体
        self.setFont(BASIC_FONT)

        # 设置允许排序
        self.setSorting(True)

        # 初始化表格
        self.initTable()

        # 注册事件监听
        self.registerEvent()

        # 初始化右键菜单
        self.menu = None
        self.initMenu()

        if self.instruments:
            self.init_rows()

    def setHeaderDict(self, headerDict):
        """设置表头有序字典"""
        self.headerDict = headerDict
        self.headerList = headerDict.keys()

    def setDataKey(self, dataKey):
        """设置数据字典的键"""
        self.dataKey = dataKey

    def setEventType(self, eventType):
        """设置监控的事件类型"""
        self.eventType = eventType

    def setFont(self, font):
        """设置字体"""
        self.font = font

    def setSaveData(self, saveData):
        """设置是否要保存数据到单元格"""
        self.saveData = saveData

    def initTable(self):
        """初始化表格"""
        # 设置表格的列数
        col = len(self.headerDict)
        self.setColumnCount(col)

        # 设置列表头
        labels = [d["chinese"] for d in self.headerDict.values() if d["display"] == 1]
        self.setHorizontalHeaderLabels(labels)

        # 关闭左边的垂直表头
        self.verticalHeader().setVisible(False)

        # 设为不可编辑
        self.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers)

        # 设为行交替颜色
        self.setAlternatingRowColors(True)

        # 设置允许排序
        self.setSortingEnabled(self.sorting)

    def init_rows(self, instruments=None):
        # 设置 合约代码
        if instruments:
            self.instruments = instruments
        if self.instruments:
            # self.setRowCount(len(self.instruments))

            for i, instrument in enumerate(self.instruments):
                self.insertRow(i)
                d = {}
                for n, header in enumerate(self.headerList):
                    content = safe_unicode(instrument)
                    cellType = self.headerDict[header]["cellType"]
                    if cellType != BaseCell:
                        content = 0.01
                    cell = cellType(content, self.data_event)
                    # print(self)
                    self.setItem(i, n, cell)
                    d[header] = cell
                self.dataDict[instrument] = d

    def registerEvent(self):
        """注册GUI更新相关的事件监听"""
        self.signal.connect(self.updateEvent)
        # print(self.eventType)
        self.data_event.register(self.eventType, self.signal.emit, self.keys)
        self.itemClicked.connect(self.item_clicked)

    def item_clicked(self, item):
        # pass
        # print(item.column(), item.data, item.row(), item.text())
        self.current_instrument_id = self.item(item.row(), 0).text().strip(" ")
        self.iid_changed_signal.emit(
            Event(EVENT_IID_CHANGED, self.current_instrument_id)
        )
        self.mainWindow.iid = self.current_instrument_id

    def updateEvent(self, event):
        """收到事件更新"""
        data = event.data
        # print(type(data))
        self.updateData(data)

    def updateData(self, data):
        """将数据更新到表格中"""
        # 如果允许了排序功能，则插入数据前必须关闭，否则插入新的数据会变乱
        if self.sorting:
            self.setSortingEnabled(False)

        # 如果设置了dataKey，则采用存量更新模式
        key = data[self.dataKey]
        if data[self.dataKey] == self.mainWindow.iid:
            self.tick_signal.emit(Event(EVENT_TICK, data))

        if not (key in self.keys):
            return
        # print(key, key not in self.dataDict)
        # 如果键在数据字典中不存在，则先插入新的一行，并创建对应单元格
        if key not in self.dataDict:
            self.insertRow(0)
            d = {}
            for n, header in enumerate(self.headerList):
                if header not in data.keys():
                    continue
                content = safe_unicode(data[header])
                cellType = self.headerDict[header]["cellType"]
                cell = cellType(content, self.data_event)

                if self.font:
                    cell.setFont(self.font)  # 如果设置了特殊字体，则进行单元格设置

                # if self.saveData:  # 如果设置了保存数据对象，则进行对象保存
                #     cell.data = data

                self.setItem(0, n, cell)
                d[header] = cell
            # print(d)
            self.dataDict[key] = d
        # 否则如果已经存在，则直接更新相关单元格
        else:
            d = self.dataDict[key]
            # print(d)
            for header in self.headerList:
                if header not in data.keys():
                    continue
                content = safe_unicode(data[header])
                cell = d[header]
                if not cell:
                    cellType = self.headerDict[header]["cellType"]
                    cell = cellType(content, self.data_event)
                cell.set_content(content, data[header])

                # if self.saveData:  # 如果设置了保存数据对象，则进行对象保存
                #     cell.data = data

        # 调整列宽
        # self.resizeColumns()

        # 重新打开排序
        if self.sorting:
            self.setSortingEnabled(True)

    def resizeColumns(self):
        """调整各列的大小"""
        self.horizontalHeader().resizeSections(
            QtWidgets.QHeaderView.ResizeMode.ResizeToContents
        )

    def setSorting(self, sorting):
        """设置是否允许根据表头排序"""
        self.sorting = sorting

    def saveToCsv(self):
        """保存表格内容到CSV文件"""
        # 先隐藏右键菜单
        self.menu.close()

        # 获取想要保存的文件名
        path, fil = QtWidgets.QFileDialog.getSaveFileName(
            self, "保存数据", "", "CSV(*.csv)"
        )

        try:
            if path:
                with open(path, "wb") as f:
                    writer = csv.writer(f)

                    # 保存标签
                    headers = [header.encode("gbk") for header in self.headerList]
                    writer.writerow(headers)

                    # 保存每行内容
                    for row in range(self.rowCount()):
                        rowdata = []
                        for column in range(self.columnCount()):
                            item = self.item(row, column)
                            if item is not None:
                                rowdata.append(item.text().encode("gbk"))
                            else:
                                rowdata.append("")
                        writer.writerow(rowdata)
        except IOError:
            pass

    def on_col_selected(self, action):
        """选择列是否显示"""
        # print(action.text(),action.isChecked())
        for i in range(self.columnCount()):
            if self.horizontalHeaderItem(i).text() == action.text():
                if action.isChecked():
                    self.setColumnHidden(i, False)
                else:
                    self.setColumnHidden(i, True)
                break

    def initMenu(self):
        """初始化右键菜单"""
        self.menu = QtWidgets.QMenu(self)
        selectDisplayItem = QtWidgets.QMenu("请选择要显示的列", self)
        itms = {
            k: {"display": v["chinese"], "checked": 1}
            for k, v in self.headerDict.items()
        }
        sdi = create_check_menu_list(self, itms)
        self.agSelectCol = QtGui.QActionGroup(self)
        for act in sdi.values():
            act.setActionGroup(self.agSelectCol)
            selectDisplayItem.addAction(act)
        self.agSelectCol.triggered.connect(self.on_col_selected)
        self.menu.addMenu(selectDisplayItem)

        saveAction = QtWidgets.QAction("保存内容", self)
        saveAction.triggered.connect(self.saveToCsv)
        self.menu.addAction(saveAction)
        if self.name != "self_selected":
            addSelfSelectedAction = QtWidgets.QAction("加入自选", self)
            addSelfSelectedAction.triggered.connect(self.addSelfSelected)
            self.menu.addAction(addSelfSelectedAction)
        else:
            delSelfSelectedAction = QtWidgets.QAction("从自选删除", self)
            delSelfSelectedAction.triggered.connect(self.delSelfSelected_triggered)
            self.menu.addAction(delSelfSelectedAction)

    def delSelfSelected_triggered(self):
        """从自选删除"""
        ss_future.discard(self.current_instrument_id)
        save_ss_instruments(ss_future)

    def addSelfSelected(self):
        """ "加入自选股"""
        ss_future.add(self.current_instrument_id)
        save_ss_instruments(ss_future)

    def contextMenuEvent(self, event):
        """右键点击事件"""
        self.menu.popup(QtGui.QCursor.pos())


def create_market_monitor(
    parent: QtWidgets.QTabWidget,
    data_event,
    main_instruments,
    market_instruments,
    selected_instruments,
    trade_instruments,
    mainWindow=None,
):
    # print(market_instruments.keys())
    if not parent:
        return
    for i in range(parent.count()):
        widget = parent.widget(i)
        if widget.objectName() == "tabMain":
            lo = QtWidgets.QHBoxLayout(parent.widget(i))
            lo.setObjectName("mainTableLO")
            table = TickTable(
                data_event,
                parent=widget,
                keys=main_instruments,
                name="mainTable",
                mainWindow=mainWindow,
            )
            lo.addWidget(table)
            mainWindow.mainTable = table
            mainWindow.mainTableLO = lo
        if widget.objectName() == "tabCZCE":
            lo = QtWidgets.QHBoxLayout(parent.widget(i))
            lo.setObjectName("CZCETableLO")
            table = TickTable(
                data_event,
                parent=widget,
                keys=market_instruments["CZCE"],
                name="CZCETable",
                mainWindow=mainWindow,
            )
            lo.addWidget(table)
            mainWindow.CZCETable = table
            mainWindow.CZCETableLO = lo
        if widget.objectName() == "tabDCE":
            lo = QtWidgets.QHBoxLayout(parent.widget(i))
            lo.setObjectName("DCETableLO")
            table = TickTable(
                data_event,
                parent=widget,
                keys=market_instruments["DCE"],
                name="DCETable",
                mainWindow=mainWindow,
            )
            lo.addWidget(table)
            mainWindow.DCETable = table
            mainWindow.DCETableLO = lo
        if widget.objectName() == "tabSHFE":
            lo = QtWidgets.QHBoxLayout(parent.widget(i))
            lo.setObjectName("SHFETableLO")
            table = TickTable(
                data_event,
                parent=widget,
                keys=market_instruments["SHFE"],
                name="SHFETable",
                mainWindow=mainWindow,
            )
            lo.addWidget(table)
            mainWindow.SHFETable = table
            mainWindow.SHFETableLO = lo
        if widget.objectName() == "tabCFFEX":
            lo = QtWidgets.QHBoxLayout(parent.widget(i))
            lo.setObjectName("CFFEXTableLO")
            table = TickTable(
                data_event,
                parent=widget,
                keys=market_instruments["CFFEX"],
                name="CFFEXTable",
                mainWindow=mainWindow,
            )
            lo.addWidget(table)
            mainWindow.CFFEXTable = table
            mainWindow.CFFEXTableLO = lo
        if widget.objectName() == "tabSS":
            lo = QtWidgets.QHBoxLayout(parent.widget(i))
            lo.setObjectName("SSTableLO")
            table = TickTable(
                data_event,
                parent=widget,
                keys=selected_instruments,
                name="SSTable",
                mainWindow=mainWindow,
            )
            lo.addWidget(table)
            mainWindow.SSTable = table
            mainWindow.SSETableLO = lo
        if widget.objectName() == "tabTrading":
            lo = QtWidgets.QHBoxLayout(parent.widget(i))
            lo.setObjectName("TradingTableLO")
            table = TickTable(
                data_event,
                parent=widget,
                keys=selected_instruments,
                name="TradingTable",
                mainWindow=mainWindow,
            )
            lo.addWidget(table)
            mainWindow.TradingTable = table
            mainWindow.TradingTableLO = lo
